﻿using System;
using System.IO;
using System.Collections.Generic;
using SocialFilter.model;
using System.Threading;

namespace SocialFilter.controller
{
	class MainController
	{
		private List<Feed> sharedFeeds = new List<Feed>();
		private Mutex feedMutex = new Mutex();
		private AutoResetEvent feedsAddedEvent = new AutoResetEvent(false);
		private Mutex datacontrollerMutex = new Mutex();
		
		public int run()
		{
			//Load configuration first
			ConfigurationController.Initialize();
			ConfigurationController.LoadConfiguration();
		
			Dictionary<string, DataSourceQuerier> queriers = new Dictionary<string, DataSourceQuerier>();	// list of queriers for FB, twitter, etc
			//TODO: add FB and twitter queriers to list of queriers
			string twitterConsumerKey = ConfigurationController.GetConfiguration("twitter_consumer_key");
			string twitterConsumerSecret = ConfigurationController.GetConfiguration("twitter_consumer_secret");
			queriers.Add("twitter", new TwitterQuerier( twitterConsumerKey, twitterConsumerSecret));
            queriers.Add("facebook", new FacebookQuerier()); 
			
			
			/*Database initialization*/
			string db_server = ConfigurationController.GetConfiguration("database_server");
			string db_username = ConfigurationController.GetConfiguration("database_username");
			string db_password = ConfigurationController.GetConfiguration("database_password");
			string db_name = ConfigurationController.GetConfiguration("database_name");
			string db_port = ConfigurationController.GetConfiguration("database_port");
			DataController.TokenKey = ConfigurationController.GetConfiguration("token_key");
			
			if ( db_port == null )
			{
				DataController.open( db_server, db_username, db_password, db_name );
			}
			else
			{
				DataController.open(db_server, db_username, db_password, db_name, db_port);
			}
			
			/*Data processing thread set up*/
			Thread dataProcessingThread = new Thread( new ThreadStart(this.process_data) );
			dataProcessingThread.Start();
			
			/*Data Querying thread set up*/
			int dataSourcePollTimer;
			if ( !int.TryParse(ConfigurationController.GetConfiguration("data_source_poll_timer"), out dataSourcePollTimer) )
			{
				dataSourcePollTimer = 30000; //default to 30 seconds
			}
			
			while (true)
			{
				// query database for all feeds the are out of date or were requested to be updated
				datacontrollerMutex.WaitOne();
					Console.Write("\nQUERYING DATA...");
					List<Feed> feeds = DataController.GetRequestedFeeds();
					feeds.AddRange(DataController.GetOutdatedFeeds());
				datacontrollerMutex.ReleaseMutex();
				// iterate over all feeds
				foreach ( Feed f in feeds )
				{
					// query datasources
					foreach( Account a in f.Accounts )
					{
						DataSourceQuerier querier;
						if ( queriers.TryGetValue( a.SiteName, out querier ) )
						{
							f.AddItems(querier.RetrieveItems(a.AccountName, a.Token));
						}
					}
				}
				Console.WriteLine("DATA QUERIED\n");
				// wait for access to sharedFeeds
				feedMutex.WaitOne();
				sharedFeeds.AddRange(feeds); // copy feeds to sharedFeeds
				// release access to feeds
				feedMutex.ReleaseMutex();
				// clear out feeds
				feeds.Clear();
				// signal that there's new data available
				feedsAddedEvent.Set();
				// then sleep for 30 seconds
				Thread.Sleep(dataSourcePollTimer);
			}
			
			datacontrollerMutex.WaitOne();
			DataController.close();
			datacontrollerMutex.ReleaseMutex();
			
			return 0;
		}
		
		private void process_data()
		{
			RSSController rssController = new RSSController(); // generates rss feeds
			FilterController filterer = new FilterController(); //filters feed items based on keyword strings
			string rssDirectory = ConfigurationController.GetConfiguration("feed_directory");
			List<Feed> feedsToProcess; //local list for storing data
			
			List<Feed> feedsToDelete; //list of feeds to delete
			List<string> successfullyDeletedFeedIds = new List<string>();
			
			List<Filter> filters;
			while ( true )
			{
				feedsAddedEvent.WaitOne(); //wait for something to be added
				{
					feedMutex.WaitOne(); //lock access to the list
					
					//Don't check if the list was empty
					//while ( sharedFeeds.Count == 0 ) //make sure the list isn't empty
					//{
					//    feedMutex.ReleaseMutex(); //release mutex
					//    feedsAddedEvent.WaitOne(); //wait for something to be added
					//    feedMutex.WaitOne(); //grab mutex and try again
					//}

					feedsToProcess = new List<Feed>(sharedFeeds); //copy data to local list
					sharedFeeds.Clear(); //clear shared list
				}
				feedMutex.ReleaseMutex();
				Console.Write("\nPROCESSING DATA...");
				// process itemsToProcess
				foreach( Feed f in feedsToProcess )
				{
					// get filters for feed
					datacontrollerMutex.WaitOne();
						filters = DataController.GetFeedFilters(f.FeedId);
					datacontrollerMutex.ReleaseMutex();
					filterer.SetFilters(filters);
					// filter items
					filterer.FilterItems(f.Items);
					// write items to rss feed
					rssController.generateFeed(f, rssDirectory);
				}
				Console.WriteLine("DATA PROCESSED\n");
				Console.Write("\nDELETING MARKED FEEDS...");
				// process any requests for deleting feed files here
				datacontrollerMutex.WaitOne();
					feedsToDelete = DataController.GetDeletedFeeds();
				datacontrollerMutex.ReleaseMutex();
				//try to delete the file related to each feed
				foreach( Feed f in feedsToDelete )
				{
					try
					{
						// delete file
						File.Delete(rssDirectory + "/" + f.Url);
						successfullyDeletedFeedIds.Add(f.FeedId);
					}
					catch ( Exception e )
					{
						//failed to delete, do NOT remove it from the database yet
					}
				}
				// remove successfully feeds from database
				datacontrollerMutex.WaitOne();
					DataController.DeleteFeeds(successfullyDeletedFeedIds);
				datacontrollerMutex.ReleaseMutex();
				successfullyDeletedFeedIds.Clear();
				Console.WriteLine("MARKED FEEDS WERE DELETED\n");
			}
		}
	}
}
